using System;
using System.Net;

namespace Stunt.Nutss.Stunt
{

	/// <summary>STUNT message sent over signaling channel.</summary>
	[Serializable]
	internal class STUNTMessage
	{

		#region Enums

		/// <summary>STUNT message type</summary>
		internal enum Stage
		{
			/// <summary>Invite remote TCP peer</summary>
			STUNT_INVITE,
			/// <summary>Accept TCP invitation</summary>
			STUNT_ACCEPT,
			/// <summary>Acknowledge TCP socket establishment</summary>
			STUNT_ESTABLISHED,
			/// <summary>Request TCP callback</summary>
			STUNT_CALLBACK,
			/// <summary>Signal TCP fatal error</summary>
			STUNT_ABORT,
			/// <summary>Request TCP active/passive role reversal</summary>
			STUNT_SWAP,
			/// <summary>Invite remote UDP peer</summary>
			STUN_INVITE,
			/// <summary>Accept UDP invitation</summary>
			STUN_ACCEPT,
			/// <summary>Acknowledge UDP socket establishment</summary>
			STUN_ESTABLISHED,
			/// <summary>Signal UDP sockets are ready</summary>
			STUN_READY,
			/// <summary>Signal UDP fatal error</summary>
			STUN_ABORT,
			/// <summary>Request UDP active/passive role reversal</summary>
			STUN_SWAP
		}

		#endregion

		#region Variables

		/// <summary>STUNTEndPointType of the STUNT signaling message</summary>
		internal Stage stage;

		/// <summary>
		/// External address and port predicted by the sender. Valid only in
		/// INVITE and ACCEPT packets.
		/// </summary>
		internal IPEndPoint pred;

		#endregion

		#region Constructor

		public STUNTMessage()
		{
		}

		#endregion

		#region Set

		/// <summary>Sets the fields in the message</summary>
		/// <param name="stage">type of the message</param>
		/// <param name="pred">predicted port, if any</param>
		/// <returns> this message</returns>
		private STUNTMessage Set(Stage stage, IPEndPoint pred)
		{
			this.stage = stage;
			this.pred = pred;
			return this;
		}

		#endregion

		#region Invite

		/// <summary>Changes message to an INVITE</summary>
		/// <param name="pred">predicted port</param>
		/// <returns>this message</returns>
		internal virtual STUNTMessage Invite(IPEndPoint pred)
		{
			return Set(Stage.STUNT_INVITE, pred);
		}

		#endregion

		#region InviteUDP

		/// <summary>Changes message to an INVITE</summary>
		/// <param name="pred">predicted port</param>
		/// <returns>this message</returns>
		internal virtual STUNTMessage InviteUDP(IPEndPoint pred)
		{
			return Set(Stage.STUNT_INVITE, pred);
		}

		#endregion

		#region Accept

		/// <summary>Changes message to an ACCEPT</summary>
		/// <param name="pred">predicted port</param>
		/// <returns>this message</returns>
		internal virtual STUNTMessage Accept(IPEndPoint pred)
		{
			return Set(Stage.STUNT_ACCEPT, pred);
		}

		#endregion

		#region AcceptUDP

		/// <summary>Changes message to an ACCEPT</summary>
		/// <param name="pred">predicted port</param>
		/// <returns>this message</returns>
		internal virtual STUNTMessage AcceptUDP(IPEndPoint pred)
		{
			return Set(Stage.STUNT_ACCEPT, pred);
		}

		#endregion

		#region Established

		/// <summary>Changes message to an ESTABLISHED</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage Established()
		{
			return Set(Stage.STUNT_ESTABLISHED, null);
		}

		#endregion

		#region EstablishedUDP

		/// <summary>Changes message to an ESTABLISHED</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage EstablishedUDP()
		{
			return Set(Stage.STUNT_ESTABLISHED, null);
		}

		#endregion

		#region Callback

		/// <summary>Changes message to an CALLBACK</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage Callback()
		{
			return Set(Stage.STUNT_CALLBACK, null);
		}

		#endregion

		#region Swap

		/// <summary>Changes message to an SWAP</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage Swap()
		{
			return Set(Stage.STUNT_SWAP, null);
		}

		#endregion

		#region SwapUDP

		/// <summary>Changes message to an SWAP</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage SwapUDP()
		{
			return Set(Stage.STUNT_SWAP, null);
		}

		#endregion

		#region Abort

		/// <summary>Changes message to an ABORT</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage Abort()
		{
			return Set(Stage.STUNT_ABORT, null);
		}

		#endregion

		#region AbortUDP

		/// <summary>Changes message to an ABORT</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage AbortUDP()
		{
			return Set(Stage.STUNT_ABORT, null);
		}

		#endregion

		#region ReadyUDP

		/// <summary>Changes message to an READY</summary>
		/// <returns>this message</returns>
		internal virtual STUNTMessage ReadyUDP()
		{
			return Set(Stage.STUN_READY, null);
		}

		#endregion

		#region ToString

		public override string ToString()
		{
			return String.Format("[stage={0} pred={1}]", stage, pred);
		}

		#endregion

	}
}